﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections;
using TrainGame.Model;
using TrainGame.Engine;

namespace TrainGame.AI.TileLay
{
	/// <summary>
	/// Build towards destination hex
	/// </summary>
	internal class HeadForDestinationTileGoal : TileLayGoal
	{
		public override string GetName(PublicCompany company)
		{
			return "HeadForDestination " + company.Destination;
		}

		/// <summary>
		/// how important is heading for our destination?
		/// </summary>
		protected override int ComputeNeed(Gameboard gameboard, PublicCompany company, Dictionary<string, GameboardHex> runToHexes, AiMoveConfig config)
		{
			if (! company.NeedDestination)
				return 0;

			if (company.Escrow > 0)
				return company.Escrow / 10;

			return 8;	// a basic small amount if nothing in escrow yet (5 was a little too low compared to Head For Toronto)
		}

		/// <summary>
		/// pick a tile lay that best heads us toward our destination
		/// </summary>
		internal override List<AiTileLay> ComputeTileLays(Stopwatch watch, TimeSpan timeLimit, Gameboard gameboard,
			PublicCompany company, List<AiTileLay> tileLays,
			Dictionary<string, GameboardHex> runToHexes, Dictionary<string, GameboardHex> tileLayHexes, AiMoveConfig config)
		{
			List<AiTileLay> bestTileLays = null;
			int bestValue = -1;

			List<string> DestHexes = GameEngine.GetConnectedHexes(gameboard, company.Destination, null);	// don't need to be able to actually reach the dest hex (i.e. ignore tokens)

			foreach (AiTileLay tileLay in tileLays)
			{
				if (watch.Elapsed.CompareTo(timeLimit) > 0)
					goto done;	// time limit expired, return the best tile lay so far

				gameboard.ApplyTileLayWithUndo(tileLay);	// add tile lay to game board, computes hex.NewRoutes
				tileLay.TransferHexNewRoutes(company, gameboard);

				ComputeTileGoalValue(tileLay, gameboard, company, runToHexes, DestHexes);

				// HACK: double-town tile lays with no upgrade/downgrade path are worse than all others
				if ((tileLay.Tile.TownType == GameboardHex.eTownType.DblTown) &&
				  (tileLay.Tile.ValidUpgrades.Count == 0) && (tileLay.Tile.ValidDowngrades.Count == 0))
					tileLay.GoalValue--;

				gameboard.UndoTileLay(tileLay);	// restore the game board

				if (tileLay.GoalValue == bestValue)
					bestTileLays.Add(tileLay);
				else if (tileLay.GoalValue > bestValue)
				{
					bestTileLays = new List<AiTileLay>();
					bestTileLays.Add(tileLay);
					bestValue = tileLay.GoalValue;
				}
			}

		done:
			return bestTileLays;
		}

		static private void ComputeTileGoalValue(AiTileLay tileLay, Gameboard gameboard, PublicCompany company,
			Dictionary<string, GameboardHex> runToHexes, List<string> destHexes)
		{
			tileLay.GoalValue = 0;
			if (destHexes.Contains(tileLay.Hex.Location))
			{
				// It hits a destination hex, does it make a destination route?
				// Temporarily update ExistingRoutes (CheckForDestination needs this)
				Hashtable existingRoutes = (Hashtable) tileLay.Hex.ExistingRoutes.Clone();
				tileLay.Hex.TransferTileRoutesToHex();	// writes ExistingRoutes

				// check for destination
				bool reached = company.ReachedDestination;
				bool really = company.ReallyApplyDestination;
				company.ReallyApplyDestination = false;
				company.CheckForDestination();
				bool madeDestination = company.ReachedDestination;

				// restore ExistingRoutes
				tileLay.Hex.ExistingRoutes.Clear();
				foreach (object oKey in existingRoutes.Keys)
					tileLay.Hex.ExistingRoutes.Add(oKey, existingRoutes[oKey]);

				// restore settings
				company.ReallyApplyDestination = really;
				company.ReachedDestination = reached;

				if (madeDestination)
				{
					tileLay.GoalValue = 50;	// this is it!
					return;
				}
			}

			// look at each new route out of this hex
			foreach (string direction in tileLay.NewRoutes)
			{
				int goal = 0;

				// get next hex
				string nextLoc = (string) tileLay.Hex.Neighbors[direction];
				if (destHexes.Contains(nextLoc))
					goal = 20;	// there it is!
				// only look at next hexes we don't currently reach
				// (if we can reach it, some other tile lay will definitely have higher value)
				else if (!runToHexes.ContainsKey(nextLoc))
				{
					GameboardHex nextHex = gameboard.GameboardHexes[nextLoc];
					if (nextHex != null)
					{
						goal = 2 * (10 - GameEngine.CalculateHexDistance(nextLoc, destHexes));
						if ((nextHex.Tile == null) && (nextHex.InitialTile.Length == 0) &&	// no initial tile either
						  nextHex.Cost == 0)	// but don't add +1 if the tile costs money
							goal += 1;	// if no tile already there, +1 (easier to build through)
					}

					// TODO: if tile connects to additional trackage that gets us closer, need to value that higher -
					// maybe as close as we can get on that trackage (where we can lay tile next turn)
				}

				if (goal > tileLay.GoalValue)
					tileLay.GoalValue = goal;
			}
		}
	}
}
